package com.zzk.www.nettyServer.server;

import com.zzk.www.nettyServer.initialize.ChatServerInitialize;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Created by zzk on 2017/12/15.
 */
public class ChatServer {

    private int port;

    public ChatServer(int port) {
        this.port = port;
    }


    public void start() {

        //对nio中的selector的封装
        //用于处理服务器端接收客户端连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();

        //进行网络通信(读写)
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            //{@link AbstractBootstrap} is a helper class that makes it easy to bootstrap a {@link Channel}. It support
            // method-chaining to provide an easy way to configure the {@link AbstractBootstrap}.
            //Bootstrap 是 Netty 提供的一个便利的工厂类, 我们可以通过它来完成 Netty 的客户端或服务器端的 Netty 初始化.
            //辅助工具类,用于服务器通道的一系列配置
            ServerBootstrap bootstrap = new ServerBootstrap();


            bootstrap.group(bossGroup, workerGroup)//绑定两个线程组
                    .channel(NioServerSocketChannel.class)//指定NIO的channel
                    .childHandler(new ChatServerInitialize())//设定子处理器
                    /**
                     * 对于ChannelOption.SO_BACKLOG的解释：
                     * 服务器端TCP内核维护有两个队列，我们称之为A、B队列。客户端向服务器端connect时，会发送带有SYN标志的包（第一次握手），服务器端
                     * 接收到客户端发送的SYN时，向客户端发送SYN ACK确认（第二次握手），此时TCP内核模块把客户端连接加入到A队列中，然后服务器接收到
                     * 客户端发送的ACK时（第三次握手），TCP内核模块把客户端连接从A队列移动到B队列，连接完成，应用程序的accept会返回。也就是说accept
                     * 从B队列中取出完成了三次握手的连接。
                     * A队列和B队列的长度之和就是backlog。当A、B队列的长度之和大于ChannelOption.SO_BACKLOG时，新的连接将会被TCP内核拒绝。
                     * 所以，如果backlog过小，可能会出现accept速度跟不上，A、B队列满了，导致新的客户端无法连接。要注意的是，backlog对程序支持的
                     * 连接数并无影响，backlog影响的只是还没有被accept取出的连接
                     */
                    .option(ChannelOption.SO_BACKLOG, 128)//设置TCP缓冲区
                    .option(ChannelOption.SO_RCVBUF, 32 * 1024)//设置接收数据缓冲大小
                    .childOption(ChannelOption.SO_KEEPALIVE, true);//保持连接

            ChannelFuture future = bootstrap.bind(this.port).sync();//同步后阻塞

            /**
             * 我们知道，在Netty中所有的I/O操作都是异步的。这意味着任何的I/O调用都将立即返回，而不保证这些被请求的I/O操作在调用结束的时候已经完成。
             * 取而代之地，你会得到一个返回的ChannelFuture实例，这个实例将给你一些关于I/O操作结果或者状态的信息。
             * */


            System.out.println("服务器已经启动...");

            future.channel().closeFuture().sync();

            System.out.println("服务器已经关闭...");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }


    }



}
